package model.collisions2d;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import model.ModelObject;

public class CollisionContainer {
	/**
	 * @invariant for each Collision c in collisions ->
	 *            watched.containsAll(c.getInvolvedObjects())
	 */
	private final Set<Collision> collisions = new HashSet<Collision>();
	private final Set<ModelObject> watched = new HashSet<ModelObject>();

	/**
	 * @invariant for each ModelObject m in toupdate -> watched.contains(m)
	 */
	private final Set<ModelObject> toupdate = new HashSet<ModelObject>();

	public boolean watchObject(ModelObject o) {
		return watched.add(o);
	}

	public boolean unWatchObject(ModelObject o) {
		boolean result = watched.remove(o);
		if (result) {
			Collection<Collision> toremove = new ArrayList<Collision>();
			for (Collision c : collisions) {
				if (c.isInvolved(o)) {
					toremove.add(c);
					//recheck the collisions this object collided with
					toupdate.add(c.getOther(o));
				}
			}
			collisions.removeAll(toremove);
		}
		return result;
	}

	/**
	 * @require isWatched(m)
	 * @param m
	 */
	public void updateObject(ModelObject m) {
		toupdate.add(m);
	}

	public boolean isWatched(ModelObject m) {
		return watched.contains(m);
	}

	public void update() {
		// remove old collisions
		Set<Collision> toremove = new HashSet<Collision>();
		for (Collision c : collisions) {
			for (ModelObject mo : toupdate) {
				if (c.isInvolved(mo)) {
					toremove.add(c);
				}
			}
		}
		toremove.removeAll(toremove);

		// update the collisions
		for (ModelObject mo : toupdate) {
			for (ModelObject other : watched) {
				if (checkCollision(mo, other)) {
					try {
						collisions.add(new Collision(mo, other));
					} catch (UniquenessException e) {} //this prevents collisions with self
				}
			}
		}
		toupdate.clear();
	}

	public static boolean checkCollision(ModelObject m1, ModelObject m2) {
		return m1.getMask().isInsideMask(m2.getMask(),
				m2.getLocation().getx() - m1.getLocation().getx(),
				m2.getLocation().gety() - m1.getLocation().gety());
	}
	
	public Set<Collision> getCollisions(ModelObject mo){
		Set<Collision> result = new HashSet<Collision>();
		for(Collision c:collisions){
			if(c.isInvolved(mo)){
				result.add(c);
			}
		}
		return result;
	}
	
	public void resetCollisions(){
		toupdate.addAll(watched);
	}
}
